﻿#include "client.h"
#include <QTimer>
#include <QTcpSocket>
#include <QString>
#include "command_parse.h"
#include "command_define.h"
#include "camera_define.h"
#include <QDataStream>
#include <QThread>
#include <QDateTime>
#include <QFile>

Client::Client(Camera *pc, QObject *parent):
    QObject(parent),
    pCamera(pc)
{
    external_trigger_success_ = new QTimer(this);
    socket = new QTcpSocket(this);

    QFile file("/etc/init.d/deviceNum");
    if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
        qDebug() << "file error";
    }
    QByteArray array = file.readAll();
    device_num_ = array.toInt();
    qDebug() << device_num_;
    file.close();

    connect(this, &Client::InternalWriteData, this, &Client::onWritData);
    connect(this, &Client::sigInitTimer, this, &Client::onInitTimer, Qt::BlockingQueuedConnection);
    connect(this, &Client::sigCloseTimer, this, &Client::onCloseTimer, Qt::BlockingQueuedConnection);
    connect(this, &Client::sigConnect, this, &Client::onConnect, Qt::BlockingQueuedConnection);
    connect(pCamera, &Camera::pictureOverTrans, this, &Client::onGetAndSendPic);
    connect(external_trigger_success_,&QTimer::timeout,this,&Client::SendExternalTriggerNum);
//    emit sendListAdd(&list);
    pCamera->getListAdd(&list);
    pCamera->SetDeviceNum(device_num_);

//    connect(socket, &QTcpSocket::stateChanged, this, &Client::onDisconnected);
}

Client::~Client()
{
//    qDebug()<<"disconnect1";
    delete timer;
    delete command_timer;
    delete heartbeat_timer;
}

void Client::InitTimer()
{
    emit sigInitTimer();
}

void Client::CloseSocket()
{
    qDebug()<<"disconnect";
//    socket->disconnectFromHost();
//    socket->close();

}

void Client::CloseTimer()
{
    emit sigCloseTimer();
}

bool Client::Connect(const QString &ip, const quint16 &port, bool &res, int ms_timeout)
{
    emit sigConnect(ip, port, res, ms_timeout);
    return res;
}

QList<QByteArray> Client::GetListCommands()
{
    QMutexLocker lock(&mutex);
    return listBuffers;
}

void Client::SetListCommands(QList<QByteArray> array)
{
    QMutexLocker lock(&mutex);
    listBuffers = array;
}

void Client::pushDataBuffer(const QByteArray &d)
{
    QMutexLocker lock(&mutex);
    listBuffers.append(d);
}

void Client::clearBuffer()
{
    QMutexLocker lock(&mutex);
    listBuffers.clear();
}
//对指令进行解析
bool Client::checkRecvCommand(const QByteArray &cmd)
{
    const char* p_data1 = (const char*)cmd.data();
    int frame_num = sizeof (Command::CommandHeader::preamble);
    bool ok;
//#ifdef BigAndSmall
//    quint16 frame = qFromBigEndian(cmd.mid(frame_num, 2).toHex().toUShort(&ok, 16));
//#else
    quint16 frame = cmd.mid(frame_num, 2).toHex().toUShort(&ok, 16);
//#endif
//    qDebug()<<"frame"<<frame;
    int code_num = sizeof (Command::CommandHeader::preamble) + sizeof (Command::CommandHeader::frameNum) + sizeof (Command::CommandHeader::cameraId);
    QByteArray data = cmd.mid(sizeof(Command::CommandHeader), cmd.size() - CommandParse::ReceiveDataMinBytes());
    switch (p_data1[code_num]) {
    case Command::RecvExternExposure:{
        CameraExternTrigger(frame, data);
        return true;
    }
    case Command::RecvCameraInfo:{
        GetCameraInfo(frame, data);
        return true;
    }
    case Command::RecvCameraMode:{
        SetCameraMode(frame, data);
        return true;
    }
    case Command::RecvTriggerSource:{
        SetTriggerSource(frame, data);
        return true;
    }
    case Command::RecvInternalTrigger:{
        TriggerOne(frame, data);
        return true;
    }
    case Command::RecvExpTime:{
        SetExposureTime(frame, data);
        return true;
    }
    case Command::RecvFrame:{
        SetFrame(frame, data);
        return true;
    }
    case Command::RecvAnalogGain:{
        SetAnologGain(frame, data);
        return true;
    }
//    case Command::RecvDigitalGain:{
//        SetDigiGain(frame, data);
//        return true;
//    }
    case Command::RecvOutputWindow:{
        SetWindow(frame, data);
        return true;
    }
    case Command::RecvTemp:{
        GetTemp(frame, data);
        return true;
    }
    case Command::RecvOutputType:{
        SetpicTMode(frame, data);
        return true;
    }
    case Command::RecvRecover:{
        RecoverCamera(frame, data);
        return true;
    }
    case Command::RecvSaveLocation:{
        SetSaveLocation(frame, data);
        return true;
    }
    case Command::RecvSetPhotoMessage:{
        SetPhotoMessage(frame, data);
        return true;
    }
    }
    return false;
}

int Client::CameraExternTrigger(quint16 frame, QByteArray data)
{
    Q_UNUSED(data)
    QByteArray arr;
    QByteArray a;
    pCamera->CameraExternTrigger();
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdExternExposure, a);
    return SendData(arr);
}

int Client::GetCameraInfo(quint16 frame, QByteArray data)
{
    Q_UNUSED(data)
    QByteArray arr;
    QByteArray a;
    CameraSpace::CameraInfo info;
    pCamera->GetCameraInfo(info);
    QDataStream stream(&a, QIODevice::WriteOnly);
#ifdef BigAndSmall
    stream << info.initResult<< info.exposureMode<<info.triggerSource<<qFromBigEndian(info.exposureTime)<<qFromBigEndian(info.frameTime)
            <<info.analogGain<<qFromBigEndian(info.picStartX)<<qFromBigEndian(info.picWidth)<<qFromBigEndian(info.picStartY)<<qFromBigEndian(info.picHeight)
           <<qFromBigEndian(info.tempx10)<<info.pictureType<<info.saveLocation<<qFromBigEndian(info.intStorageSize)<<qFromBigEndian(info.outStorageSize);
#else
    stream << info.initResult<< info.exposureMode<<info.triggerSource<<info.exposureTime<<info.frameTime
            <<info.analogGain<<info.picStartX<<info.picWidth<<info.picStartY<<info.picHeight
           <<info.tempx10<<info.pictureType<<info.saveLocation<<info.intStorageSize<<info.outStorageSize;
#endif
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdCameraInfo, a);
    return SendData(arr);
}

int Client::SetCameraMode(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    unsigned char mode = data[0];
    unsigned char current_mode;
    pCamera->SetCameraMode(mode, current_mode);
    a.append(current_mode);
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdCameraMode, a);
    return SendData(arr);
}

int Client::SetTriggerSource(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    unsigned char mode = data[0];
    unsigned char current_mode;
    pCamera->SetTriggerSource(mode, current_mode);
    a.append(current_mode);
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdTriggerSource, a);
    if(1 == current_mode){
//        qDebug() << "CmdInternalTrigger";
        external_trigger_success_->stop();
    }else{
//        qDebug() << "external_trigger";
        external_trigger_success_->start(external_trigger_success_timeout_);
    }
//    qDebug() << "trigger_source" << current_mode;
    return SendData(arr);
}

int Client::TriggerOne(quint16 frame, QByteArray data)
{
//    qDebug() << "triggerOne!!!!!!!!!!!!!!!!!!!!!" << QDateTime::currentDateTime().toString("yyyyy-MM-dd hh:mm:ss.zzz");
    QByteArray arr;
    QByteArray a;
    bool ok;
    list.clear();
    quint32 mode = data.toHex().toUInt(&ok,16);
    if(Command::None == mode){
        pCamera->TriggerStop();
        arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdInternalTrigger, a);
        SendData(arr);
    }else if(Command::OnePhoto == mode){
        pCamera->TriggerOne(list);
        arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdInternalTrigger, a);
        SendData(arr);
    }else if(Command::MorePhoto == mode){
        pCamera->TriggerMore(list);
        arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdInternalTrigger, a);
        SendData(arr);
    }

//    qDebug() << "triggerOne over..." << QDateTime::currentDateTime().toString("yyyyy-MM-dd hh:mm:ss.zzz");
    return Command::CsCommandOk;
}

int Client::SetExposureTime(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    bool ok;
    //toHex()将字节数组转换为十六进制字符串
    quint16 time = data.toHex().toUInt(&ok,16);
#ifdef BigAndSmall
    time = qFromBigEndian(time);
#endif
//    qDebug() << time << "exposure_time.....";
    quint16 current_time;
    pCamera->SetExposureTime(time, current_time);
#ifdef BigAndSmall
    current_time = qFromBigEndian(current_time);
#endif
    a.append(CommandParse::ShortToArray(current_time));
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdExpTime, a);
//    qDebug() << arr.toHex();
    return SendData(arr);
}

int Client::SetFrame(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    bool ok;
    quint16 frame_rate = data.toHex().toUShort(&ok, 16);
#ifdef BigAndSmall
    frame_rate = qFromBigEndian(frame_rate);
#endif

    quint16 current_frame_rate;
    pCamera->SetFrame(frame_rate, current_frame_rate);
#ifdef BigAndSmall
    current_frame_rate = qFromBigEndian(current_frame_rate);
#endif
    a.append(CommandParse::ShortToArray(current_frame_rate));
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdFrame, a);
    return SendData(arr);
}

int Client::SetAnologGain(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    unsigned char gain = data[0];
    unsigned char current_gain;
    pCamera->SetAnologGain(gain, current_gain);
    a.append(current_gain);
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdAnalogGain, a);
    return SendData(arr);
}

//int Client::SetDigiGain(quint16 frame, QByteArray data)
//{
//    QByteArray arr;
//    QByteArray a;
//    unsigned char gain = data[0];
//    unsigned char current_gain;
//    pCamera->SetDigiGain(gain, current_gain);
//    a.append(current_gain);
//    arr = CommandParse::BuildSendData(frame, 0x01, Command::CmdDigitalGain, a);
//    return SendData(arr);
//}

int Client::SetWindow(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    CameraSpace::CameraWindow window;
    QDataStream stream(&data, QIODevice::ReadOnly);
    stream >> window.picStartX>> window.picWidth>>window.picStartY>>window.picHeight;
#ifdef BigAndSmall
    window.picStartX = qFromBigEndian(window.picStartX);
    window.picWidth = qFromBigEndian(window.picWidth);
    window.picStartY = qFromBigEndian(window.picStartY);
    window.picHeight = qFromBigEndian(window.picHeight);
#endif
    CameraSpace::CameraWindow current_window;
    pCamera->SetWindow(window, current_window);
#ifdef BigAndSmall
    a.append(CommandParse::ShortToArray(qFromBigEndian(current_window.picStartX)));
    a.append(CommandParse::ShortToArray(qFromBigEndian(current_window.picWidth)));
    a.append(CommandParse::ShortToArray(qFromBigEndian(current_window.picStartY)));
    a.append(CommandParse::ShortToArray(qFromBigEndian(current_window.picHeight)));
#else
    a.append(CommandParse::ShortToArray(current_window.picStartX));
    a.append(CommandParse::ShortToArray(current_window.picWidth));
    a.append(CommandParse::ShortToArray(current_window.picStartY));
    a.append(CommandParse::ShortToArray(current_window.picHeight));
#endif
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdOutputWindow, a);
//    qDebug() << "arr" << arr;
    return SendData(arr);
}

int Client::GetTemp(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray recv;
    QByteArray a;
    quint16 temp;
    pCamera->GetTemp(temp);
#ifdef BigAndSmall
    temp = qFromBigEndian(temp);
#endif
    a.append(CommandParse::ShortToArray(temp));
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdTemp, a);
    return SendData(arr);

}

int Client::SetpicTMode(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    unsigned char mode = data[0];
    unsigned char current_mode;
    pCamera->SetpicTMode(mode, current_mode);
    a.append(current_mode);
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdOutputType, a);
    return SendData(arr);
}

int Client::RecoverCamera(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    unsigned char res;
    pCamera->RecoverCamera(res);
    a.append(res);
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdRecover, a);
    return SendData(arr);
}

int Client::SetSaveLocation(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
    unsigned char mode = data[0];
    unsigned char current_mode;
    pCamera->SetSaveLocation(mode, current_mode);
    a.append(current_mode);
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdSaveLocation, a);
    return SendData(arr);
}

int Client::SetPhotoMessage(quint16 frame, QByteArray data)
{
    QByteArray arr;
    QByteArray a;
//    unsigned int index = CommandParse::ArrayToInt(data);
    QByteArray current_photo_message;
    pCamera->SetPhotoMessage(data, current_photo_message);
    a.append(current_photo_message);
    arr = CommandParse::BuildSendData(frame, device_num_, Command::CmdSetPhotoMessage, a);
    return SendData(arr);
}

int Client::SendHeartbeat()
{
    QByteArray arr;
    QByteArray a;
    arr = CommandParse::BuildSendData(0x01, device_num_, Command::CmdHeartBeat, a);
    return SendData(arr);
}

int Client::SendData(const QByteArray &data)
{
    emit InternalWriteData(data);
    return Command::CsCommandOk;
}

int Client::SendExternalTriggerNum()
{
    QByteArray arr;
    QByteArray a;
    if(pCamera->DetectionExternalTrigger()){
        arr = CommandParse::BuildSendData(0x00, device_num_, Command::CmdExternExposure, a);
//        qDebug() << arr;
        return SendData(arr);

    }
    return 0;




}

void Client::onScanServer()
{
    socket->connectToHost(serverIp, serverPort);
    //等待连接成功
    if(!socket->waitForConnected(nTimeOut))
    {
        socket->disconnect();
    }else{
        //连接QTcpSocket的信号槽，以读取新数据
        timer->stop();
        command_timer->start(100);
        heartbeat_timer->start(1000);
        qDebug()<<"connect sucess";
        QObject::connect(socket, &QTcpSocket::readyRead, this, &Client::OnReadData);
    }
}

void Client::OnReadData()
{
    QMutexLocker lock(&mutex);
    lastRemainData.append(socket->readAll());
//    qDebug()<<"lastRemainData"<<lastRemainData.toHex();
    QList<QByteArray> list_data;
    CommandParse::ParseCommands(lastRemainData, list_data);
    for(auto &v : list_data){
        listBuffers.append(v);
    }
}

void Client::onCommandParse()
{
    QList<QByteArray> commands = GetListCommands();//获得数据
    QList<QByteArray> temp_commands = commands;
    if(commands.size() != 0){
        //            qDebug()<<"commands"<<commands;
        for(auto &v : commands){
            if(checkRecvCommand(v)){
                //should handl the recv data
                temp_commands.removeOne(v);
                SetListCommands(temp_commands);
            }
        }
    }
}

void Client::onWritData(const QByteArray &data)
{
//    qDebug()<<"write"<<data.toHex();
    int res = socket->write(data);
    socket->flush();
    if(res == -1){
        qDebug()<<"write wrong";
        socket->disconnect();
        heartbeat_timer->stop();
        command_timer->stop();
        timer->start();
    }
    //    socket->flush();
}

//void Client::onDisconnected(QAbstractSocket::SocketState state)
//{
//    qDebug()<<"disconnect"<<state;
//}

void Client::onHeartbeat()
{
    SendHeartbeat();
}

void Client::onInitTimer()
{
    timer = new QTimer(this);
    command_timer = new QTimer(this);
    heartbeat_timer = new QTimer(this);
    //检测连接超时的心跳
    connect(timer, &QTimer::timeout, this, &Client::onScanServer);
    //接收指令的心跳
    connect(command_timer, &QTimer::timeout, this, &Client::onCommandParse);
    connect(heartbeat_timer, &QTimer::timeout, this, &Client::onHeartbeat);
}

void Client::onCloseTimer()
{
        timer->stop();
        command_timer->stop();
        heartbeat_timer->stop();
}

void Client::onConnect(const QString &ip, const quint16 &port, bool &res, int ms_timeout)
{
    if(!timer->isActive()){
        serverIp = ip;
        serverPort = port;
        nTimeOut = ms_timeout;
//        timer->setInterval(1000);
        timer->start(1000);
    }
    res = true;
}

void Client::onGetAndSendPic()
{
//    qDebug() << "triggerOneEnterOver!!!!!!!!!!!!!!!!!!!!!" << QDateTime::currentDateTime().toString("yyyyy-MM-dd hh:mm:ss.zzz");
    QByteArray arr;
    for(int i =0; i < list.size(); i++){
        QByteArray arr;
        arr = CommandParse::BuildSendData(1, device_num_, Command::CmdPicture, list.at(i));
        SendData(arr);
//        list.removeAt(i);
    }
    list.clear();
//qDebug() << "triggerOneOver!!!!!!!!!!!!!!!!!!!!!" << QDateTime::currentDateTime().toString("yyyyy-MM-dd hh:mm:ss.zzz");
}
